package model.tokens;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import state.TabState;

import model.expression.AbstractExpression;
import model.parser.ParserException;
import model.parser.ParsingData;

import expression.Expression;

/**
 * model.tokens that operate on other tokens
 * 
 * @author KevinWang Chris James Misha Lavrov
 * 
 * @param <T>
 *            any object
 */
public class OperatorToken extends AbstractToken {
	protected ParsingData myParsingData;

	public OperatorToken(String value, ParsingData parsingData) {
		myParsingData = parsingData;
	}

	public ParsingData getParsingData() {
		return myParsingData;
	}

	@Override
	public void convertToPostFix(Stack<AbstractToken> opStack,
			Stack<AbstractExpression> outStack,TabState ts,Stack<AbstractExpression> out) {
		
		//done Via shunting yard
		while (!opStack.isEmpty() && opStack.peek() instanceof OperatorToken) {
			OperatorToken topOperator = (OperatorToken) (opStack.peek());

			if (topOperator.getParsingData().takesPriorityOver(myParsingData)) {
				((OperatorToken)opStack.pop()).makeExpression(outStack,out);
			} else
				break;
		}

		opStack.push(this);

	}

	public void makeExpression(Stack<AbstractExpression> outStack, Stack<AbstractExpression> out) {

		List<AbstractExpression> childrenList = new ArrayList<AbstractExpression>();

		for (int i = 0; i < myParsingData.getNumArguments(); i++) {
			if (outStack.isEmpty()){
				if(!out.isEmpty())
					childrenList.add(out.pop());
				else throw ParserException.BAD_ARGUMENTS;
			}else
				childrenList.add(0, outStack.pop());
		}

		AbstractExpression newExpr = null;

		try {
			Class<?> exprClass = myParsingData.getExpressionClass();
			Constructor<?> constructor = exprClass.getConstructor(List.class);
			newExpr = (AbstractExpression) constructor.newInstance(childrenList);
		} catch (Exception e) {
			throw ParserException.BAD_REFLECTION;
		}

		outStack.push(newExpr);
	}
}
